#ifndef _XSTRUCT_HPP
#define _XSTRUCT_HPP

//#include <cstdio>
//#include <iostream>
#include "type_provider.hpp"
//#include <cmath>

/*
 * known issues:
 *  - each field must be defined in separate line (therefore we cannot define several fields with one macro)
 *  - fields must be defined in one file (cannot define part of structure in one included file, and another part in another one)
 */

/* to be documented:
 * XSTRUCT_BEGIN, FIELD, END
 * XSTRUCT_DERIVED_BEGIN
 *
 * x_for_each (4)
 * x_get
 *
 * TYPE_PROVIDER
 *
 *
 */

enum { X_STRUCT_COUNTER0= __COUNTER__};

template<int C>
struct UniqueId
{};


template<class T1, class T2>
struct NonVoid
{
	typedef T1 Result;
};

template<class T2>
struct NonVoid<void, T2>
{
	typedef T2 Result;
};


namespace xstruct
{
template<class STRUCT_TAG, class TYPE_PROVIDER>
struct Typed {};

struct XBase {
	template<class OPERATOR>
		void x_for_each(OPERATOR &o) {}
	template<class OPERATOR>
		void x_for_each() {};
	template<class OPERATOR>
		void x_for_each(OPERATOR &o) const {}
	template<class OPERATOR>
		void x_for_each() const {};
	template<class OPERATOR>
		static void x_for_each_s(OPERATOR &o) {}
	template<class OPERATOR>
		static void x_for_each_s() {};
}; 

} /*namespace xstruct*/
	namespace xstruct {	\
//	template<class TYPE_PROVIDER>	\
//	struct Typed<name, TYPE_PROVIDER> { typedef name ## _typed<TYPE_PROVIDER > Type; };	
	}
	

//	template<class TYPE_PROVIDER>	\
	struct XStruct<name, TYPE_PROVIDER>: name ## _typed<TYPE_PROVIDER> {};	\
	\


#define XSTRUCT_BEGIN(name)	\
	XSTRUCT_DERIVED_BEGIN(name, ::xstruct::XBase)

#define XSTRUCT_DERIVED_BEGIN(name, base_type)	\
	template<class TYPE_PROVIDER, class TAG_CANDIDATE >	\
	struct name ## _typed;	\
	template<class TYPE_PROVIDER, class TAG_CANDIDATE=name##_typed<DefaultTypeProvider, void> >	\
	struct name ## _typed;	\
	\
	typedef name ## _typed<DefaultTypeProvider, name##_typed<DefaultTypeProvider, void> > name;	\
	\
	template<class TYPE_PROVIDER, class TAG_CANDIDATE >	\
	struct name ## _typed: base_type {	\
		typedef typename NonVoid<TAG_CANDIDATE, name##_typed<TYPE_PROVIDER, TAG_CANDIDATE> >::Result Tag;	\
		static const char *x_get_name() {return #name; }	\
		private: enum { XID_0 = __COUNTER__  };	\
		/* x_for_each */	\
		public:		\
		template<class OPERATOR>	\
		void x_for_each_i(OPERATOR &o, UniqueId<XID_0>) { base_type::x_for_each(o); }	\
		template<class OPERATOR>	\
		void x_for_each_i(UniqueId<XID_0>) { base_type::x_for_each<OPERATOR>();  }	\
		template<class OPERATOR>	\
		void x_for_each_i(OPERATOR &o, UniqueId<XID_0>) const { base_type::x_for_each(o); }	\
		template<class OPERATOR>	\
		void x_for_each_i(UniqueId<XID_0>) const { base_type::x_for_each<OPERATOR>();  }	\
		template<class OPERATOR>	\
		static void x_for_each_is(OPERATOR &o, UniqueId<XID_0>) { base_type::x_for_each_s(o); }	\
		template<class OPERATOR>	\
		static void x_for_each_is(UniqueId<XID_0>) { base_type::x_for_each_s<OPERATOR>();  }	\

#define XSTRUCT_FIELD(type, name)\
	public: \
		struct name ## _tag_candidate { typedef type DefaultType; typedef Tag StructTag; static const char *get_name() {return #name;} };	\
		typedef typename Tag::name##_tag_candidate name##_tag;	\
		typename TYPE_PROVIDER::template Field<name##_tag>::Type name;\
	private: \
		enum { XID_ ## name= __COUNTER__  };	\
		typename TYPE_PROVIDER::template Field<name##_tag>::Type &x_get(name##_tag *) { return name; }\
		/* x_for_each */	\
		template<class OPERATOR>\
		void x_for_each_i(OPERATOR &o, UniqueId<XID_ ## name>) { x_for_each_i(o, UniqueId<XID_ ## name-1>());  o.template run<name ## _tag>(name); } \
		template<class OPERATOR>\
		void x_for_each_i(UniqueId<XID_ ## name>) { x_for_each_i<OPERATOR>(UniqueId<XID_ ## name-1>());  OPERATOR::template run<name ## _tag>(name); } \
		template<class OPERATOR>\
		void x_for_each_i(OPERATOR &o, UniqueId<XID_ ## name>) const { x_for_each_i(o, UniqueId<XID_ ## name-1>());  o.template run<name ## _tag>(name); } \
		template<class OPERATOR>\
		void x_for_each_i(UniqueId<XID_ ## name>) const { x_for_each_i<OPERATOR>(UniqueId<XID_ ## name-1>());  OPERATOR::template run<name ## _tag>(name); } \
		template<class OPERATOR>\
		static void x_for_each_is(OPERATOR &o, UniqueId<XID_ ## name>) { x_for_each_is(o, UniqueId<XID_ ## name-1>());  o.template run<name ## _tag>(); } \
		template<class OPERATOR>\
		static void x_for_each_is(UniqueId<XID_ ## name>) { x_for_each_is<OPERATOR>(UniqueId<XID_ ## name-1>());  OPERATOR::template run<name ## _tag>(); }


#define XSTRUCT_END()	\
	private:	\
	enum { XID_XFINISH = __COUNTER__ };	\
	/* x_for_each */	\
	template<class OPERATOR, int C>	\
	void x_for_each_i(OPERATOR &o, UniqueId<C>)	\
	{ x_for_each_i(o, UniqueId<C-1>());	}	\
	template<class OPERATOR, int C>	\
	void x_for_each_i(UniqueId<C>)	\
	{ x_for_each_i<OPERATOR>(UniqueId<C-1>());	}	\
	template<class OPERATOR, int C>	\
	void x_for_each_i(OPERATOR &o, UniqueId<C>)	const \
	{ x_for_each_i(o, UniqueId<C-1>());	}	\
	template<class OPERATOR, int C>	\
	void x_for_each_i(UniqueId<C>) const	\
	{ x_for_each_i<OPERATOR>(UniqueId<C-1>());	}	\
	template<class OPERATOR, int C>	\
	static void x_for_each_is(OPERATOR &o, UniqueId<C>)	\
	{ x_for_each_is(o, UniqueId<C-1>());	}	\
	template<class OPERATOR, int C>	\
	static void x_for_each_is(UniqueId<C>)	\
	{ x_for_each_is<OPERATOR>(UniqueId<C-1>());	}	\
	public:	\
	/* x_for_each */	\
	template<class OPERATOR>	\
	void x_for_each(OPERATOR &o)	\
	{x_for_each_i(o, UniqueId<XID_XFINISH>());	}	\
	template<class OPERATOR>	\
	void x_for_each()	\
	{x_for_each_i<OPERATOR>(UniqueId<XID_XFINISH>());	}	\
	template<class OPERATOR>	\
	void x_for_each(OPERATOR &o) const	\
	{x_for_each_i(o, UniqueId<XID_XFINISH>());	}	\
	template<class OPERATOR>	\
	void x_for_each() const	\
	{x_for_each_i<OPERATOR>(UniqueId<XID_XFINISH>());	}	\
	template<class OPERATOR>	\
	static void x_for_each_s(OPERATOR &o)	\
	{x_for_each_is(o, UniqueId<XID_XFINISH>());	}	\
	template<class OPERATOR>	\
	static void x_for_each_s()	\
	{x_for_each_is<OPERATOR>(UniqueId<XID_XFINISH>());	}	\
	public:	\
	/* x_get */	\
	template<class FIELD_TAG>	\
	typename TYPE_PROVIDER::template Field<FIELD_TAG>::Type &x_get()	\
	{return x_get((FIELD_TAG *)0);}	\
	};


#endif
